<?php

namespace App\Services;

use App;
use App\AwardApplicationFormData;
use App\Project;
use App\ProjectAttachment;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;

class FileService
{
    public function __construct()
    {
    }

    /**
     * 从项目表单数据中解析出附件记录, 同步至 project_attachments 表
     *
     * @param Project $project
     */
    public function syncProjectAttachmentsFromFormData(Project $project)
    {
        ProjectAttachment::where('project_id', $project->id)->delete();
        $formData = json_decode(AwardApplicationFormData::find($project->id)->form_data);
        foreach ($formData->attachments as $type => $filepath) {
            $format = null;
            $filesize = 0;
            $filename = null;
            if ($filepath) {
                $pathinfo = pathinfo($filepath);
                $format = strtolower($pathinfo['extension']);
                $filename = $pathinfo['basename'];
                $realPath = public_path($filepath);
                if (file_exists($realPath)) {
                    $filesize = filesize($realPath);
                    // 特殊情况处理: 没有主文件名, 如 .doc
                    if (starts_with($filename, '.') && $filesize > 0) {
                        $correctedDirPath = dirname($filepath) . '/auto_corrected';
                        $realCorrectedDirPath = public_path($correctedDirPath);
                        if (!file_exists($realCorrectedDirPath)) {
                            $this->debug("$realCorrectedDirPath created");
                            mkdir($realCorrectedDirPath, 0777, true);
                        }
                        $typeName = config('app.award_attachment_type_name')[$project->award_id][$type];
                        $correctedFilename = "$typeName.$format";
                        $correctedFilepath = "$correctedDirPath/$correctedFilename";
                        $correctedFileRealPath = public_path($correctedFilepath);
                        copy($realPath, $correctedFileRealPath);
                        $this->debug("Corrected $realPath -> $correctedFileRealPath");
                        $filename = $correctedFilename;
                        $filesize = filesize($correctedFileRealPath);
                        $filepath = $correctedFilepath;
                    }
                }
            }
            $attachment = ProjectAttachment::create([
                'project_id' => $project->id,
                'award_id' => $project->award_id,
                'type' => $type,
                'format' => $format,
                'is_root_file' => true,
                'filesize' => $filesize,
                'filename' => $filename,
                'filepath' => $filepath,
            ]);
            $this->processCompressedAttachment($attachment);
        }
    }

    private function processCompressedAttachment(ProjectAttachment $attachment)
    {
        if ($attachment->has_decompressed) {
            return;
        }
        if (!in_array($attachment->format, ['zip', 'rar'])) {
            return;
        }
        if (empty($attachment->filesize)) {
            return;
        }
        $this->debug("Process compressed attachment");
        $pathinfo = pathinfo($attachment->filepath);
        $decompressedFilesDir = $pathinfo['dirname'] . '/' . str_replace([' ', '(', ')'], '_', $pathinfo['filename']) . '_decompressed';
        $decompressedFilesDirRealPath = public_path($decompressedFilesDir);
        if (!file_exists($decompressedFilesDirRealPath)) {
            mkdir($decompressedFilesDirRealPath, 0777, true);
        }
        $this->debug("  Decompress files to $decompressedFilesDirRealPath");
        $fileRealPath = public_path($attachment->filepath);
        $this->decompress($fileRealPath, $decompressedFilesDirRealPath, $attachment->format);
        $decompressedFiles = $this->scanDecompressedFilesRecursively($decompressedFilesDirRealPath);
        $decompressedAttachmentCount = 0;
        $decompressedAttachmentFileSizeSum = 0;
        $this->debug("Compressed files:");
        foreach ($decompressedFiles as $decompressedFileRealPath) {
            $decompressedFileRealPathInfo = pathinfo($decompressedFileRealPath);
            $decompressedFileFormat = strtolower($decompressedFileRealPathInfo['extension']);
            if (in_array($decompressedFileFormat, ['zip', 'rar', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'pdf', 'jpg', 'jpeg', 'gif', 'png', 'tif', 'bmp'])) {
                $decompressedFilePath = substr($decompressedFileRealPath, strlen(public_path()));
                $this->debug($decompressedFilePath);
                $decompressedAttachment = ProjectAttachment::create([
                    'project_id' => $attachment->project_id,
                    'award_id' => $attachment->award_id,
                    'type' => $attachment->type,
                    'format' => $decompressedFileFormat,
                    'is_root_file' => false,
                    'filesize' => filesize($decompressedFileRealPath),
                    'filename' => $decompressedFileRealPathInfo['basename'],
                    'filepath' => $decompressedFilePath,
                ]);
                $decompressedAttachmentCount += 1;
                $decompressedAttachmentFileSizeSum += $decompressedAttachment->filesize;
            }
        }
        if ($decompressedAttachmentFileSizeSum) {
            $attachment->update([
                'has_decompressed' => true,
                'sub_file_count' => $decompressedAttachmentCount,
                'sub_filesize_sum' => $decompressedAttachmentFileSizeSum,
            ]);
        }
    }

    private function decompress($filePath, $destDirPath, $format)
    {
        $filePath = str_replace([' ', '(', ')'], ['\\ ', '\\(', '\\)'], $filePath);
        $destDirPath = str_replace(' ', '\\ ', $destDirPath);
        if ($format == 'zip') {
            passthru("unar -f -D -o $destDirPath $filePath");
        } else if ($format == 'rar') {
            passthru("unrar x -o+ $filePath $destDirPath");
        } else {
            throw new \InvalidArgumentException("Unsupported archive format: $format");
        }
    }

    private function scanDecompressedFilesRecursively($dir)
    {
        $rii = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir));
        $files = [];
        foreach ($rii as $file) {
            if ($file->isDir()) {
                continue;
            }
            $files[] = $file->getPathname();
        }
        return $files;
    }

    private function debug($message)
    {
        if (App::runningInConsole()) {
            echo "$message\n";
        }
    }

}